#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <semaphore.h>
#include <errno.h>
#include <time.h>

#include "ringBuffer.h"

//编译
//gcc main.c ringBuffer.c -lpthread
//对比
//cmp read_file_len write_file_len
//cmp read_file write_file

pthread_t thd_rd[15],thd_wr[4];

#define IF_USE_RD_LOCK 1U
#define IF_USE_WR_LOCK 1U
#if IF_USE_RD_LOCK
pthread_mutex_t thd_rd_write_file_mutex;
#endif
#if IF_USE_WR_LOCK
pthread_mutex_t thd_wr_write_file_mutex;
#endif

static void* task_read(void *para);
static void* task_write(void *para);
static uint32_t get_tick_count_ms(void);

struct ringBuf_t *ringBuf_handle;
uint8_t buf[32 * 1024 * 1024] = {0};

//必须是2的幂次方
#define TEST_DATA_SIZE 512
uint8_t test_write_data[TEST_DATA_SIZE] = {0},test_read_data[TEST_DATA_SIZE] = {0};

sem_t sem;

FILE *f_write,*f_read;
FILE *f_write_len,*f_read_len;

volatile uint32_t wr_thread_is_runing = 1;

int main(int argc,char *argv[])
{
    int ret;

    #if IF_USE_WR_LOCK
    pthread_mutex_init(&thd_wr_write_file_mutex,NULL);
    #endif
    #if IF_USE_RD_LOCK
    pthread_mutex_init(&thd_rd_write_file_mutex,NULL);
    #endif

    ret = sem_init(&sem,0,0);
    if(ret < 0)
    {
        perror("sem_init");
    }

    f_write = fopen("write_file","w+");
    if(!f_write)
    {
        perror("fopen write_file");
    }
    f_read = fopen("read_file","w+");
    if(!f_read)
    {
        perror("fopen read_file");
    }

    f_write_len = fopen("write_file_len","w+");
    if(!f_write_len)
    {
        perror("fopen write_file_len");
    }
    f_read_len = fopen("read_file_len","w+");
    if(!f_read_len)
    {
        perror("fopen read_file_len");
    }

    printf("%p\r\n",ringBuf_handle);
    RingBufInit(&ringBuf_handle,NULL,sizeof(buf));
    printf("%p\r\n",ringBuf_handle);

    for(uint32_t i = 0;i < sizeof(test_write_data);i++)
    {
        test_write_data[i] = rand() % (0x39 - 0x30 + 1) + 0x30;
        usleep(1000);
    }

    for(uint32_t i = 0;i < sizeof(thd_rd) / sizeof(*thd_rd);i++)
    {
        pthread_create(&thd_rd[i],NULL,task_read,NULL);
    }
    for(uint32_t i = 0;i < sizeof(thd_wr) / sizeof(*thd_wr);i++)
    {
        pthread_create(&thd_wr[i],NULL,task_write,NULL);
    }

    for(uint32_t i = 0;i < sizeof(thd_wr) / sizeof(*thd_wr);i++)
    {
        pthread_join(thd_wr[i],NULL);
    }
    wr_thread_is_runing = 0;
    for(uint32_t i = 0;i < sizeof(thd_rd) / sizeof(*thd_rd);i++)
    {
        pthread_join(thd_rd[i],NULL);
    }

    sem_destroy(&sem);
    fclose(f_write);
    fclose(f_read);
    fclose(f_write_len);
    fclose(f_read_len);

    return 0;
}

void* task_read(void *para)
{
    int ret;
    struct timespec timeout = {0};
    size_t size;
    uint32_t rd_len;
    char tmpStr[32] = {0};
    static uint32_t read_failed_times = 0,read_success_times = 0;

    //计算相对时间
    clock_gettime(CLOCK_REALTIME,&timeout);
    timeout.tv_sec += 1;

    for(;;)
    {
        ret = sem_timedwait(&sem,&timeout);
        if(ret < 0)
        {
            if((errno == EINTR) || (errno == EAGAIN))
            {
                continue;
            }
            else if(errno = ETIMEDOUT)
            {
                if(!wr_thread_is_runing)
                {
                    break;
                }
                else
                {
                    continue;
                }
            }
            else
            {
                perror("sem_timedwait");
            }
        }

        #if IF_USE_RD_LOCK
        pthread_mutex_lock(&thd_rd_write_file_mutex);
        #endif

        rd_len = RingReadDataFromBuffer(&ringBuf_handle,test_read_data,sizeof(test_read_data));
        if(!rd_len)
        {
            ++read_failed_times;
        }
        else
        {
            ++read_success_times;

            size = fwrite(test_read_data,rd_len,1,f_read);
            if(size < 1)
            {
                printf("thread[%lu] read write file failed..\r\n",pthread_self());
            }
            else
            {
                sprintf(tmpStr,"%u\t",rd_len);
                if(!(read_success_times % 32))
                {
                    sprintf(tmpStr + strlen(tmpStr),"\n");
                }
                size = fwrite(tmpStr,strlen(tmpStr),1,f_read_len);
                if(size < 1)
                {
                    printf("thread[%lu] read write len to file failed..\r\n",pthread_self());
                }
//                printf("read:%s\t",tmpStr);
            }
        }

        #if IF_USE_RD_LOCK
        pthread_mutex_unlock(&thd_rd_write_file_mutex);
        #endif

        //模拟数据处理
        usleep(1000);
    }

    printf("\r\nthread[%lu] read_failed_times:%u,read_success_times:%u,total:%u\r\n",pthread_self(),read_failed_times,read_success_times,read_failed_times + read_success_times);
    printf("thread[%lu] task_read exit..\r\n",pthread_self());
}

void* task_write(void *para)
{
    size_t size;
    uint32_t wr_len = 0;
    char tmpStr[32] = {0};
    static uint32_t write_failed_times = 0,write_success_times = 0;

    srand(get_tick_count_ms());
    for(uint32_t i = 0,cond = rand() % (512 - 256 + 1) + 256;i < cond;i++)
    {
        #if IF_USE_WR_LOCK
        pthread_mutex_lock(&thd_wr_write_file_mutex);
        #endif

        srand(get_tick_count_ms());
        wr_len = rand() & (TEST_DATA_SIZE - 1);
//        wr_len = test_write_data[i & 0xff];
        if(!RingWriteDataToBuffer(&ringBuf_handle,test_write_data,wr_len))
        {
            ++write_failed_times;
        }
        else
        {
            ++write_success_times;

            size = fwrite(test_write_data,wr_len,1,f_write);
            if(size < 1)
            {
                printf("thread[%lu] write write file failed..\r\n",pthread_self());
            }
            else
            {
                sprintf(tmpStr,"%u\t",wr_len);
                if(!(write_success_times % 32))
                {
                    sprintf(tmpStr + strlen(tmpStr),"\n");
                }
                size = fwrite(tmpStr,strlen(tmpStr),1,f_write_len);
                if(size < 1)
                {
                    printf("thread[%lu] write write len to file failed..\r\n",pthread_self());
                }
//                printf("write:%s\t",tmpStr);
            }
            sem_post(&sem);
        }

        #if IF_USE_WR_LOCK
        pthread_mutex_unlock(&thd_wr_write_file_mutex);
        #endif

        //模拟数据接收
        usleep(100);
    }

    printf("\r\nthread[%lu] write_failed_times:%u,write_success_times:%u,total:%u\r\n",pthread_self(),write_failed_times,write_success_times,write_failed_times + write_success_times);
    printf("thread[%lu] task_write exit..\r\n",pthread_self());
}

uint32_t get_tick_count_ms(void)
{
    struct timespec tp;

    clock_gettime(CLOCK_MONOTONIC,&tp);

    return (tp.tv_sec * 1000 + tp.tv_nsec / 1000000);
}
